home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume89 / aplictns / nro.1 < prev    next >
Internet Message Format  |  1989-02-03  |  64KB

  1. Path: xanth!ames!mailrus!ulowell!page
  2. From: page@swan.ulowell.edu (Bob Page)
  3. Newsgroups: comp.sources.amiga
  4. Subject: v89i019:  nro - nroff-like text formatter, Part01/02
  5. Message-ID: <11542@swan.ulowell.edu>
  6. Date: 3 Feb 89 05:00:45 GMT
  7. Organization: University of Lowell, Computer Science Dept.
  8. Lines: 1887
  9. Approved: page@swan.ulowell.edu
  10.  
  11. Submitted-by: U211344@HNYKUN11.BITNET (Olaf 'Rhialto' Seibert)
  12. Posting-number: Volume 89, Issue 19
  13. Archive-name: applications/nro.1
  14.  
  15. #    This is a shell archive.
  16. #    Remove everything above and including the cut line.
  17. #    Then run the rest of the file through sh.
  18. #----cut here-----cut here-----cut here-----cut here----#
  19. #!/bin/sh
  20. # shar:    Shell Archiver
  21. #    Run the following text with /bin/sh to create:
  22. #    nro.n
  23. #    nrocmd.c
  24. # This archive created: Mon Jan 30 20:00:32 1989
  25. cat << \SHAR_EOF > nro.n
  26. .tm Entering macro file
  27. .so an
  28. .tm Entering main file
  29. .TH NRO 1 "AM*GA Programmer's Manual" "KosmoSoft" "Version 1.5"
  30. .de cmd
  31. !sp $1;!ne 2;!ti -5;!bo "$0
  32. .en
  33. .de fun
  34. .sp $1;.ne 2;.ti -5;.bo "$0
  35. .en
  36. .SH NAME
  37. nro - text formatter
  38. .SH SYNOPSIS
  39. .in +5;.ta +0;.ti -5
  40. .bo "nro@t[-n] [+n] [-pxx] [-v] [-bx] [-rnx] [-rdx] [-rpx]
  41. .bo "[-mmfile] ifile ... [>ofile]
  42. .in -5;.ta
  43. .SH DESCRIPTION
  44. .ul "NRO
  45. is a text processor and formatter based on the design
  46. provided in
  47. .bo ""Software Tools"
  48. by Kernighan and Plauger. The text and commands found in the
  49. .cu "ifile(s)
  50. are processed to generate formatted text. The output may be directed into a
  51. file or to the printer, otherwise, the output will appear at the user
  52. console.
  53.  
  54. The
  55. .ul "+n
  56. option causes the output to start with page
  57. .ul "n.
  58. The
  59. .ul "-n
  60. option causes the output to stop after page
  61. .ul "n.
  62.  
  63. The
  64. .ul "-v
  65. option prints the version number to the console. Specifying a higher
  66. numerical argument makes
  67. .bo "NRO
  68. more and more verbose.
  69.  
  70. The
  71. .ul "-p
  72. option causes the output to be shifted to the right by
  73. .ul "xx
  74. spaces. This has the same effect as the
  75. .bo "@.po
  76. command.
  77.  
  78. The
  79. .ul "-b
  80. option with argument
  81. .ul "x
  82. controls if backspaces appear in the output text when underlining or
  83. overstriking. This has the same effect as the
  84. .bo "@.bs
  85. command with the same argument.
  86.  
  87. The
  88. .ul "-r
  89. options allow you to specify how much memory is allocated for macro
  90. definitions and related things. The
  91. .ul "-rn
  92. option lets you specify how many macros you may define while your text is
  93. being formatted. Default is 512. The
  94. .ul "-rd
  95. option lets you specify how much memory is reserved for keeping the
  96. contents of your macro definitions. Default is 20480 characters. The
  97. .ul "-rp
  98. option lets you specify how much characters may be pushed back into the
  99. input, when processing a macro evaluation. Default is 1024 characters.
  100. .br
  101. If any of these options is present they must be located before the first
  102. file to be processed to have any effect.
  103.  
  104. The
  105. .ul "-m
  106. option processes the file
  107. .ul "mfile
  108. for macro definitions. Note that files processed in this way should contain
  109. only macro definitions, no immediate output should be generated from this
  110. file.
  111. .PP
  112. Commands typically are distinguished by a period in column one of the input
  113. followed by a two character abbreviation for the command funtion. The
  114. abbreviation may then be followed by an optional numeric or character
  115. argument.
  116. .br
  117. The numeric argument may be an absolute value such as setting the right
  118. margin to a particular column, or the argument may be preceded by an
  119. operator to indicate that the parameter should be modified relative to a
  120. previous setting. You may use the operators +, -, /, *, % (mod), | (bitwise
  121. or), & (bitwise and), = (equal), < (less than) and > (greater than) to
  122. calculate values.
  123. .ul "No
  124. normal operator precedence is taken into account; the expression is
  125. evaluated strictly from left to right. You may use parentheses to group
  126. sub-expressions together. Note that all operators are dyadic, even the -.
  127. To generate a negative value, use a form like 0-5. A leading operator in an
  128. expression is not used in the evaluation. Instead, it has the effect of
  129. altering a previously set value. The + operator increases the set value
  130. with the given value, the - operator decreases the set value with the given
  131. value, the * operator multiplies the set value with the given value, and
  132. the / operator divides the set value by the given value. A leading operator
  133. within parentheses is ignored.
  134. .PP
  135. Commands that have effect on a given number of input lines, can also take a
  136. text argument, if it is preceded by a double quote character `"'. This
  137. behaves exactly as if you gave the number 1, and followed the command with
  138. the given text argument.
  139. .br
  140. More text or commands may follow a command if they are seperated by a
  141. semicolon `;', unless the first command must be the last one on an input
  142. line.
  143.  
  144. .tm ...commands
  145. The following commands are recognized:
  146. .in +5;.ta +0;.cc !
  147. !*---------------------------*
  148. !cmd .bo
  149. @tcauses the following lines of text to appear in boldface. The optional
  150. argument specifies the number of lines to be typed in boldface. An argument
  151. of zero explicitly turns boldface off, while a negative argument turns it
  152. on indefinitely. When overstrike is being used to produce the boldface,
  153. boldface and underlining are mutually exclusive features, and the
  154. appearance of a boldface command will cause any underlining to cease.
  155. !*---------------------------*
  156. !cmd .bp
  157. @tcauses succeeding text to appear at the top of a new page. The optional
  158. argument specifies the page number for the new page. The initial value is
  159. one and the default value is one more than the previous page number.
  160. !*---------------------------*
  161. !cmd .br
  162. @tcauses succeeding text to start on a new line at the current left margin.
  163. There is no argument for this command.
  164. !*---------------------------*
  165. !cmd .bs
  166. @tenables or disables the appearance of backspaces in the output text. A
  167. value of
  168. !cu "zero
  169. doesn't use backspaces at all, but utilizes standard ISO printer codes.
  170. The Commodore Amiga console and printer devices support these directly, but
  171. on other systems you need a post-processor to convert these standard ISO
  172. codes into, for example, Epson printer codes. If you don't want this,
  173. underlining and boldface can be done by inserting character - backspace -
  174. character combinations into the output. This is fine for devices which
  175. properly recognize the backspace character. Many printers, however, do not
  176. recognize backspaces, so the option is provided to overprint one line
  177. buffer with another. The first line buffer is terminated with just a
  178. carriage return rather than the carriage return - linefeed combination.
  179. !br;An argument of
  180. !cu "2
  181. to the backspace command removes backspaces from the output. Even with
  182. printers which do recognize backspaces, this usually is faster. An argument
  183. of
  184. !cu "1
  185. puts them into the output. The default is to use Commodore Amiga (ISO)
  186. control sequences.
  187. !*---------------------------*
  188. !cmd .cc
  189. @tchanges the
  190. !ul "NRO
  191. command character to that specified by the character argument. If no
  192. argument is provided, the default is a period.
  193. !*---------------------------*
  194. !cmd .ce
  195. @tcauses the next line of text to appear centered on the output. This
  196. automatically generates a break. The optional argument specifies if more
  197. than one line is to be centered. An argument of zero explicitly turns
  198. centering off, while a negative argument turns it on indefinitely.
  199. !*---------------------------*
  200. !cmd .cu
  201. @tcauses the next line(s) of text to be continuously underlined. Unlike the
  202. underline command (see
  203. !bo ".ul)
  204. which underlines only alphanumerics, continuous underlining underlines all
  205. printable characters. The optional argument specifies the number of lines
  206. of text to underlined. An argument of zero explicitly turns continuous
  207. underline off, while a negative argument turns it on indefinitely. Any
  208. normal underlining command currently in effect will be terminated.
  209. !*---------------------------*
  210. !cmd .de
  211. @tcauses all text and commands following to be used to define a macro. The
  212. definition is terminated by a line with
  213. !bo ".en
  214. as the first three characters. The rest of that line is ignored.
  215. !br;The first argument following the
  216. !bo ".de
  217. command becomes the name of the new command.
  218. !br;It should be noted that upper and lower case arguments are
  219. considered different. Thus, the commands
  220. !bo ".PP
  221. and
  222. !bo ".pp
  223. could define two different macros. Care should be exercised since existing
  224. commands and macros may be redefined. A macro may contain up to ten
  225. arguments. In the macro definition, the placement of arguments is
  226. designated by the two character sequences, $0, $1, ... $9.
  227. !br
  228. When the macro is invoked, each argument of the macro command line is
  229. substituted for its corresponding designator in the expansion. The first
  230. argument of the macro command is substituted for the $0 in the expansion,
  231. the second argument for the $1, and so forth. Arguments are typically
  232. strings which do not contain blanks or tabs. If an argument is to contain
  233. blanks, then it should be surrounded by either single or double quotes.
  234. !br
  235. A macro name may be at most ten characters long. If more are supplied, the
  236. results are unpredictable. To get things like $4 in the macro text, use a
  237. double $$, i.e. $$4.
  238. !*---------------------------*
  239. !cmd .ef
  240. @tspecifies the text for the footer on even numbered pages. The format is
  241. the same as for the footer command (see
  242. !bo ".fo).
  243. !*---------------------------*
  244. !cmd .eh
  245. @tspecifies the text for the header on even numbered pages. The format is
  246. the same as for the footer command (see
  247. !bo ".fo).
  248. !*---------------------------*
  249. !cmd .el
  250. @tSee
  251. !bo ".if
  252. and
  253. !bo ".ie.
  254. The
  255. !bo ".el
  256. command reverses the truth of the condition remembered by the last
  257. !bo ".ie.
  258. command, and if the result is true, it accepts the input on the remainder
  259. of the line, just like the
  260. !bo ".if
  261. command. Multi-line else-parts are thus also possible. You may have
  262. multiple
  263. !bo ".el
  264. request following a single
  265. !bo ".ie
  266. command. Since every
  267. !bo ".el
  268. reverses the remembered condition, you have if effect multiple 'then-' and
  269. 'else-parts', just divided into bits and pieces.
  270. !*---------------------------*
  271. !cmd .ev
  272. @tenvironment switch. If a numerical argument is given, the number of the
  273. current environment is pushed on a special environment number stack, and
  274. the named environment is made current. If no argument is supplied, the
  275. previous environment number that was in effect will be restored. As a
  276. variation of this, the command
  277. !bo ".ev -
  278. will restore the previous environment and
  279. !ul "discard
  280. the current environment. This is useful if you don't need the particular
  281. environment anymore. At a new invocation of this environment it will have
  282. default values again.
  283. !br
  284. The environment is the set of values that determine the appearance of
  285. formatted output. There are ten environments available, and up to nineteen
  286. environment numbers can be pushed. Because a stack of previous environment
  287. numbers is maintained, always return to a previous environment by a
  288. !bo ".ev
  289. command without numeric argument.
  290. !br
  291. The following commands affect values that are in the environment:
  292. !bo 2
  293. .ls .ti .in .rm .ce .ul .cu .it .ta .ju .nj .bo .fi .nf .pn .pc .cc and
  294. !bo ".c2.
  295. !br
  296. Also in the environment are the last remembered conditional,
  297. !bo ".ie,
  298. and collected partial output lines.
  299. !*---------------------------*
  300. !cmd .en
  301. @tdesignates the end of a macro definition.
  302. !*---------------------------*
  303. !cmd .fi
  304. @tcauses the input text to be rearranged or filled to obtain the maximum
  305. word count possible between the previously set left and right margins. No
  306. argument is expected.
  307. !*---------------------------*
  308. !cmd .fo
  309. @tspecifies text to be used for a footer. The footer text contains three
  310. strings seperated by a delimiter character. The first non-blank character
  311. following the command is designated as the delimiter. The first text string
  312. is left justified to the current indentation value (specified by
  313. !bo ".in).
  314. The second string is centered between the current indentation value and the
  315. current right margin value (specified by
  316. !bo ".rm).
  317. The third string is right justified to the current right margin value. The
  318. absence of footer text will result in the footer being printed as one blank
  319. line. The presence of the page number character (set by
  320. !bo ".pc)
  321. in the footer text results in the current page number being inserted at
  322. that position. Multiple occurrances of the page number character are
  323. allowed. This command must be the last command on an input line.
  324. !*---------------------------*
  325. !cmd .he
  326. @tspecifies text to be used for a header. The format is the same as for the
  327. footer (see
  328. !bo ".fo).
  329. !*---------------------------*
  330. !cmd .ie
  331. @tSame as
  332. !bo ".if,
  333. but remembers the resulting condition, that can be used by subsequent
  334. !bo ".el
  335. requests. Only one level of conditions is remembered: they cannot be
  336. nested. Every occurrence of a
  337. !bo ".ie
  338. request overrides the result as remembered by the previous one.
  339. !*---------------------------*
  340. !cmd .if
  341. @tConditional acceptance of input. The request is followed by a
  342. conditional, which may take several forms. Depending on the condition,
  343. subsequent input may be accepted or ignored. The following forms of
  344. condition are valid:
  345. !in +9;!ti -4;!nj
  346. !bo ".if [!]<letter>
  347. anything
  348. !ti -4;!bo ".if [!]<expression>
  349. anything
  350. !ti -4;!bo ".if [!]<delimiter> <string1> <delimiter> <string2> <delimiter>
  351. anything
  352. !in -9;!ju
  353. The following <letter>s may be used:
  354. !bo "o:
  355. Current page number is odd;
  356. !bo "e:
  357. Current page number is even;
  358. !bo "n:
  359. Formatter is
  360. !ul "NRO,
  361. which is always true;
  362. !bo "t:
  363. Formatter is TRO(FF), which is always false.
  364. !br
  365. An <expression> is said to be true if it is > 0.
  366. !br
  367. The last form is a string comparison, which is true if <string1> and
  368. <string2> are exactly equal. Any <delimiter> may be used as long as it
  369. doesn't make the conditional look like one of the other forms.
  370. !br
  371. A not-symbol `!' may immediately precede the condition to reverse its
  372. truth.
  373. !br
  374. Normally, the accepted or ignored input affected is only the rest of the
  375. current input line. A multi-line `then-part' must begin with the opening
  376. delimiter @@{ and the last line must end with the closing delimiter @@}. It
  377. may also be useful to conceal the newline following the opening delimiter
  378. if it is at the end of an input line. Because the delimiters are deleted
  379. from the input, make sure that the line is not empty without them, because
  380. this would cause a break. It is sufficient to place them on a line with a
  381. comment command to avoid this.
  382. !*---------------------------*
  383. !cmd .in
  384. @tindents the left margin to the column value specified by the argument.
  385. The default left margin is set to zero. This command performs a break.
  386. !*---------------------------*
  387. !cmd .it
  388. @tcauses the following lines of text to appear in italics. The optional
  389. argument specifies the number of lines to be typed in italics. An argument
  390. of zero explicitly turns italics off, while a negative argument turns it on
  391. indefinitely. This command is
  392. !bo "not
  393. functional when the Commodore Amiga (ISO) command sequences are not being
  394. used, since it can't be emulated by overstriking printlines.
  395. !*---------------------------*
  396. !cmd .ju
  397. @tcauses blanks to be inserted between words in a line of output in order
  398. to align or justify the right margin. The default is to justify. No
  399. argument is expected.
  400. !*---------------------------*
  401. !cmd .ls
  402. @tsets the line spacing to the value specified by the argument. The default
  403. is for single spacing.
  404. !*---------------------------*
  405. !cmd .m1
  406. @tspecifies the number of lines in the header margin. This is the space
  407. from the physical top of page to and including the header text. A value of
  408. zero causes the header to not be printed. A value of one causes the header
  409. to appear at the physical top of page. Larger argument values cause the
  410. appropriate number of blank lines to appear before the header is printed.
  411. !*---------------------------*
  412. !cmd .m2
  413. @tspecifies the number of blank lines to be printed between the header line
  414. and the first line of the processed text.
  415. !*---------------------------*
  416. !cmd .m3
  417. @tspecifies the number of blank lines to be printed between the last line
  418. of processed text and the footer line.
  419. !*---------------------------*
  420. !cmd .m4
  421. @tspecifies the number of lines in the footer margin. This command affects
  422. the footer the same way the
  423. !bo ".m1
  424. command affects the header.
  425. !*---------------------------*
  426. !cmd .ne
  427. @tspecifies a number of lines which should not be broken across a page
  428. boundary. If the number of lines remaining on a page is less than the value
  429. needed, then a new output page is started.
  430. !*---------------------------*
  431. !cmd .nf
  432. @tspecifies that succeeding text should be printed without rearrangement,
  433. or with no fill. The default is to justify. No argument is expected.
  434. !*---------------------------*
  435. !cmd .nj
  436. @tspecifies that no attempt should be made to align or justify the right
  437. margin. No argument is expected.
  438. !*---------------------------*
  439. !cmd .nr
  440. @tcauses the value of a number register to be set or modified. A total of
  441. twenty-six number registers are available designated @@na through @@nz
  442. (either upper or lower case is allowed). When the sequence @@nc is imbedded
  443. in the text, the current value of number register c replaces the sequence,
  444. thus, such things as paragraph numbering can be accomplished with relative
  445. ease.
  446. !*---------------------------*
  447. !cmd .of
  448. @tspecifies the text for the footer on odd numbered pages. The format is
  449. the same as the footer command (see
  450. !bo ".fo).
  451. !*---------------------------*
  452. !cmd .oh
  453. @tspecifies the text for the header on odd numbered pages. The format is
  454. the same as the footer command (see
  455. !bo ".fo).
  456. !*---------------------------*
  457. !cmd .pc
  458. @tspecifies the page number character to be used in headers and footers.
  459. The occurrance of this character in the header or footer text results in
  460. the current page number being printed. The default for this character is
  461. the hash mark `#'.
  462. !*---------------------------*
  463. !cmd .pl
  464. @tspecifies the page lenght or the number of lines per output page. The
  465. default is 66.
  466. !*---------------------------*
  467. !cmd .pn
  468. @tspecifies the way page numbering is done. You may specify the following
  469. values: 0 uses normal arabic page numbers, 1 generates lowercase roman
  470. numbers, and 2 specifies uppercase roman numbers. Default is arabic page
  471. numbering.
  472. !*---------------------------*
  473. !cmd .po
  474. @tspecifies a page offset value. This allows the formatted text to be
  475. shifted to the right by the number of spaces specified. This feature may
  476. also be invoked by a switch on the command line. All horizontal positions
  477. (like tabstops, indentations) are adjusted accordingly.
  478. !*---------------------------*
  479. !cmd .rm
  480. @tsets the column value for the right margin. The default is 80.
  481. !*---------------------------*
  482. !cmd .so
  483. @tcauses input to be retrieved from the file specified by the command's
  484. character string argument. The contents of the new file are inserted into
  485. the output stream until an EOF is detected. Processing of the original file
  486. is then resumed. File nesting is allowed up to a reasonable level (four).
  487. !*---------------------------*
  488. !cmd .sp
  489. @tspecifies a number of blank lines to be output before printing the next
  490. line of text. This automatically generates a break. You cannot move
  491. upwards.
  492. !*---------------------------*
  493. !cmd .ta
  494. @ttab settings. This command allows you to set tabstops. It may have
  495. optional numeric arguments. When issued without arguments, all tabstops are
  496. removed. When issued with one or more numeric arguments, tabstops are set
  497. at the specified number of spaces from the left hand side of the paper. An
  498. argument may optionally be preceded with a `+' to indicate that a distance
  499. from the previously mentioned tab is meant instead of an absolute position.
  500. A `+' sign before the first argument indicates a distance from the current
  501. indent.
  502. !*---------------------------*
  503. !cmd .ti
  504. @ttemporarily alters the indentation or left margin value for a single
  505. succeeding line of output text. This command performs a break. If an input
  506. line starts with spaces, this has the effect that a break is generated, and
  507. a temporary indent for the number of spaces is set.
  508. !*---------------------------*
  509. !cmd .tm
  510. @twrites a message to the standard error output, so you see it in your
  511. console window. This command must be the last one on an input line. When
  512. used with the no-break command character, this `no-break' gets another
  513. meaning: don't break the formatted output if it also is printed on the
  514. console. The command is ignored instead.
  515. !*---------------------------*
  516. !cmd .ul
  517. @tunderlines the alphanumeric text in the following line(s). The optional
  518. argument specifies the number of lines to be underlined. An argument of
  519. zero explicitly turns underlining off, while a negative argument turns it
  520. on indefinitely. When overstrike is being used to produce the underline,
  521. underlining and boldface are mutually exclusive features, and the
  522. appearance of an underline command cancels any existing boldface
  523. operations.
  524. !*---------------------------*
  525. !cmd .un
  526. @tundefines a previously defined macro, and all macros that were defined
  527. later. If this macro was a redefinition of an older macro with the same
  528. name, the old definition will be available again. Thus, macros are defined
  529. in a stack-like fashion.
  530. !cmd .*
  531. !cmd ." 0
  532. @tBoth of these serve as a way to insert comments in the input text. They
  533. have absolutely no effect on the output.
  534. !*---------------------------*
  535.  
  536. !cc
  537. .in -5;.PP
  538. .tm End of commands
  539. Instead of having the command immediately at the beginning of a line, you
  540. may also begin a line with a command character, then some blanks, and then
  541. again a command character, this time followed immediately by the command.
  542. There exists even a second command character, called the
  543. .bo "no break command character,
  544. `'', which suppresses the break normally generated by some commands. This
  545. no break command character can only be used in this way. If the first
  546. non-blank character is
  547. .ul "not
  548. a command character, it is considered to be text. An example illustrates
  549. this.
  550.  
  551. .RS 5;.nf
  552. @.    .sp   @@" This is a space command,
  553. @.    this is some normal text,
  554. @.    'sp 3 @@" and this spaces without a break.
  555. .RE;.fi
  556. .PP
  557. When a line does not begin with the command character, its words are placed
  558. one by one on an output line. As soon as a word doesn't fit, the collected
  559. line is printed, possibly after justification. The word is then placed on
  560. the next output line. If a line is about to be printed at the top of a
  561. page, the page header, if any, is printed first. If a line is on the last
  562. usable line of a page, the page footer, if any, is printed next, and the
  563. page is ejected.
  564. .br
  565. If you want to begin an input text line with an instance of the command
  566. character, you may precede it with the escape character.
  567. .PP
  568. A number of translations can be performed on the input, even before it is
  569. interpreted as either text or commands. Such a translation takes place
  570. where an escape character `@@' is seen in the input text. The single
  571. charachter following the escape character determines the effect.
  572.  
  573. .tm ...functions
  574. The following functions are currently implemented:
  575. .in +5
  576. .fun @@@@
  577. @tis a single @@.
  578. .*---------------------------*
  579. .fun @@e
  580. @tis replaced by the current value of the escape character. Currently,
  581. there is no way to change it.
  582. .*---------------------------*
  583. .fun @@n
  584. @tfollowed by a single letter, is replaced by the contents of the
  585. designated number register.
  586. .*---------------------------*
  587. .fun @@t
  588. @tis replaced with a tab character, to be used in conjunction with the
  589. .bo "@.ta
  590. command. You may also use a normal tab character if you wish. If a tab is
  591. encountered, the necessary space is generated by non-breakable spaces. Any
  592. spaces on the output line before the tab are also made non-breakable. This
  593. is to avoid justification spoiling the tab.
  594. .br
  595. Tabs beyond the current right margin or beyond the last tab stop are
  596. ignored, except that they separate words.
  597. .*---------------------------*
  598. .fun @@X(expression)
  599. @tis replaced with the character with character code value `expression'.
  600. .*---------------------------*
  601. .fun @@(space)
  602. @tis replaced by a non-breakable space. It behaves just like any printable
  603. character, you just don't see it. This space won't be modified by
  604. justification, and it will be underlined by continous underline.
  605. .*---------------------------*
  606. .fun @@(newline)
  607. @tis deleted from the input. If a @@ is placed at the end of an input line,
  608. it will appear as if the next input line actually is following the contents
  609. of the current line. This is called `concealing the newline'.
  610. .*---------------------------*
  611. .fun @@.
  612. @twhere . stands for the current command character, produces the command
  613. character. It will, however, not be recognized as such. This provides a way
  614. to start your input line with a command character that in fact is a text
  615. line. This only works if the command character is different from any other
  616. valid character following the escape character, and does not work for
  617. .bo "@.en
  618. commands that end a macro definition.
  619. .*---------------------------*
  620. .fun @@{
  621. .fun @@} 0
  622. @tThese two delimiters were already mentioned in the section about the
  623. .bo "@.if
  624. and
  625. .bo "@.el
  626. commands. They serve to delimit the lines you want to be affected by these
  627. commands. Normally, when the condition is false, the
  628. .bo "@.if
  629. skips input until it finds the end of the current line. If it sees the @@{
  630. following the condition, it sets a flag to indicate to the low-level file
  631. reader, that it must count these delimiters. All input is then skipped
  632. until the matching closing delimiter @@} is found. So, the
  633. .bo "@.if
  634. command never 'sees' anything of it. Also in this case, the
  635. .bo "@.if
  636. command skips the rest of the line remaining after the closing @@}.
  637. .br
  638. On the other hand, if the condition evaluates to be true, all and any
  639. opening and closing delimiters are ignored completely. The command
  640. following the condition is executed, and also of course any commands on
  641. following lines. Since a command (or text) is expected on the same line as
  642. the
  643. .bo "@.if
  644. command, omission of a command makes it look like there is an empty line,
  645. and this generates an unexpected break. Therefore, you should conceal the
  646. newline at the end of such a line. (See @@(newline).)
  647. .*---------------------------*
  648. .fun @@"
  649. @tThis is yet another way to insert a comment. Note that text may precede
  650. the comment function. All text from the comment function until the end of
  651. the line will be ignored. Note that spaces between the last word on a line
  652. and the comment are not deleted, and this may effect your layout in some
  653. (as of yet unimplemented) cirumstances.
  654.  
  655. .in -5
  656. Any unrecognized character that follows the @@ will be left in the input.
  657. .PP
  658. Macro expansion is accomplished in the following way. There exists an input
  659. push-back buffer. If any input is needed, this push-back buffer is examined
  660. first. If a character is present, the request is satisfied by taking the
  661. last pushed back character. Only if the push back buffer is empty, a
  662. character is read from the input file. At any time that
  663. .ul "NRO
  664. reads a character that is unexpected, it is pushed back, in the hope that
  665. it can be used later.
  666. .br
  667. When a macro is to be expanded, its body is pushed back, while at the same
  668. time subsituting the arguments. Normally when an instance of the escape
  669. character is to be pushed back, it is `guarded' against re-substitution
  670. when it is read back, by doubling it, so anything pushed back will be read
  671. back exactly the same. This is not done
  672. .ul "only
  673. for macro bodies, to allow resubstitution to occur in them. This means that
  674. the body of a macro is interpreted twice, once at definition time, and once
  675. at application time. The macro arguments are interpreted only once, so that
  676. a double escape character in an argument will show up in the resulting text
  677. as a single escape character, instead of disappearing. This seems to be the
  678. most intuitive approach. It will disallow some more complex applications
  679. but avoids needing four @@'s in an argument if you want only one.
  680. .tm ...examples
  681. .SH EXAMPLES
  682. .SS "Macros:
  683. You may even define a macro that defines a macro, in the following way:
  684. .br;.RS +5;.nf
  685. @.de keep
  686. @.de $0
  687. $1 $2 $3 $4 $5 $6 $7 $8 $9
  688. @@@@.en
  689. @.en
  690. .fi;.RE
  691. This macro defines a macro with a name designated by the first argument,
  692. and containing the text designated by the remaining arguments. Remember
  693. that you can always enclose an argument with quotes.
  694. .br
  695. Note that you must use two escape characters to make the
  696. .bo "@.en
  697. part of the macro body. This is because macro definitions are handled
  698. slightly different from normal input lines. In the definition of `keep'
  699. there will be a line with
  700. .bo "@@.en,
  701. and at the time of definition of the inner macro the second escape
  702. character will be stripped off, so that the end of the inner macro
  703. definition is recognized.
  704. .br
  705. Another way to to it, is to change the command character temporarily while
  706. defining the outer macro.
  707.  
  708. .SS "Conditionals:
  709. Here are a few examples of correct use of the
  710. .bo "@.if, .ie
  711. and
  712. .bo "@.el
  713. commands:
  714. .br;.RS +5;.nf
  715.  
  716. @.if @@na Register A is greater than zero!
  717.  
  718. @.if @@na @@{.firstcommand
  719. @.       .secondcommand
  720. @.       .lastcommand @@}
  721.  
  722. @.if @@na @@{@@
  723. @.       .firstcommand
  724. @.       .secondcommand
  725. @.       .lastcommand @@}
  726.  
  727. @.ie @@na @@{.firstcommand
  728. @.       .secondcommand
  729. @.       .lastcommand @@}
  730. @.el Register A is NOT greater than zero!!
  731. @.el Maybe a bit late, but register A IS greater than zero!!
  732. .fi;.RE
  733. .tm ...undocumented features
  734. .SH "UNDOCUMENTED FEATURES"
  735. .PP
  736. <censored>
  737. SHAR_EOF
  738. cat << \SHAR_EOF > nrocmd.c
  739. /*
  740.  *      Command processor for NRO text processor
  741.  *
  742.  *      Originally by Stephen L. Browning, 5723 North Parker Avenue
  743.  *      Indianapolis, Indiana 46220
  744.  *
  745.  *      Transformed beyond immediate recognition, and
  746.  *      adapted for Amiga by Olaf Seibert, KosmoSoft
  747.  *
  748.  *      Vossendijk 149-1 (study)   Beek 5 (home)
  749.  *      5634 TN  Nijmegen          5815 CS  Merselo
  750.  *      The Netherlands            The Netherlands
  751.  *      Phone:
  752.  *             (...-31)80561045     (...-31)4786205
  753.  *          or 080-561045           04786-205
  754.  *
  755.  *      This program is NOT in the public domain. It may, however
  756.  *      be distributed only at no charge, and this notice must be
  757.  *      included unaltered.
  758.  */
  759.  
  760. #include <stdio.h>
  761. #include "nro.h"
  762. #include "nroxtrn.h"
  763.  
  764. uchar *skipbl();
  765. uchar *skipwd();
  766.  
  767. /*
  768.  *      Communicating the current file through a global
  769.  *      variable is a bit of a kludge. We could as well use
  770.  *      it everywhere instead of passing it as a parameter
  771.  *      all the time. But that would not be very `structured',
  772.  *      whatever that may be.
  773.  */
  774.  
  775. comand(word)
  776. uchar *word;
  777. {
  778.         int ct, val;
  779.         int spval;
  780.         uchar argtyp, chr;
  781.         uchar *macexp;
  782.  
  783.         val = getcmdwrd(word, infile);
  784.         if (word[0] == env.c2chr) {
  785.                 env.dontbrk = TRUE;
  786.                 word++;
  787.         } else if (word[0] == env.cmdchr) {
  788.                 word++;
  789.         } else if (val > 0) {
  790.                 putbak(' ');
  791.                 pbstr(word);
  792.                 return;
  793.         }
  794.  
  795.         ct = comtyp(word, &macexp);
  796.  
  797.         if (ct == UNKNOWN) {
  798.                 error("nro: unrecognized command %s\n", word);
  799.                 env.dontbrk = FALSE;
  800.                 return;
  801.         }
  802.         if (! (ct & NOARGS))
  803.                 val = getval(&argtyp, infile);  /* Eat more of line */
  804.  
  805.         switch (ct & ~NOARGS) {
  806.         case BO: /* Bold face */
  807.                 set(&env.boval, val, argtyp, 1, -1, HUGE);
  808.                 if (env.boval)  env.reqmode |= FXBO;
  809.                 else            env.reqmode &= ~FXBO;
  810.                 if (dc.bsflg != BSAMIGA)
  811.                         env.cuval = env.ulval = 0;
  812.                 break;
  813.         case BP: /* Begin page */
  814.                 if (pg.lineno > 0) space(pg.plval);
  815.                 set(&pg.curpag, val, argtyp, pg.curpag+1, -HUGE, HUGE);
  816.                 pg.newpag = pg.curpag;
  817.                 break;
  818.         case BR: /* Break */
  819.                 dobrk();
  820.                 break;
  821.         case BS: /* Backspaces in output: 0=No, Amiga; 1=Yes; 2=Use CR */
  822.                 set(&dc.bsflg, val, argtyp, 1, 0, 2);
  823.                 break;
  824.         case C2: /* No-break command character */
  825.                 while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  826.                 if (iseol(chr)) env.c2chr = C2CHAR;
  827.                 else { env.c2chr = chr; ct = 0; }
  828.                 break;
  829.         case CC: /* Command character */
  830.                 while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  831.                 if (iseol(chr)) env.cmdchr = CMDCHAR;
  832.                 else { env.cmdchr = chr; ct = 0; }
  833.                 break;
  834.         case CE: /* Center */
  835.                 dobrk();
  836.                 set(&env.ceval, val, argtyp, 1, -1, HUGE);
  837.                 break;
  838.         case COMMENT:
  839.                 while (chr = ngetc(infile), isnteol(chr));
  840.                 break;
  841.         case CU: /* Continuous underline */
  842.                 set(&env.cuval, val, argtyp, 1, -1, HUGE);
  843.                 if (env.cuval)  env.reqmode |= FXUL;
  844.                 else            env.reqmode &= ~FXUL;
  845.                 if (dc.bsflg != BSAMIGA)
  846.                         env.ulval = env.boval = 0;
  847.                 break;
  848.         case DE: /* Define macro */
  849.                 defmac(word, infile);
  850.                 break;
  851.         case EF: /* Even footer */
  852.                 gettl(infile, pg.efoot, NULL, &pg.eflim[0], NULL);
  853.                 break;
  854.         case EH: /* Even header */
  855.                 gettl(infile, pg.ehead, NULL, &pg.ehlim[0], NULL);
  856.                 break;
  857.         case EL: /* Else */
  858.                 doel(infile);
  859.                 break;
  860.         case EN: /* End macro definition */
  861.                 error("nro: missing .de command\n");
  862.                 break;
  863.         case EV: /* Environment switch */
  864.                 if (isdigit(argtyp)) {  /* Supplied argument: push */
  865.                         if (dc.envsp >= ENVSTACK-1) {
  866.                                 error("nro: cannot push environment.\n"); break;
  867.                         }
  868.                         spval = dc.envstack[dc.envsp];  /* Current environment */
  869.                         dc.envstack[++dc.envsp] = val;  /* Save number on stack*/
  870.                         storenv(spval);                                 /* Save current envir. */
  871.                         loadenv(val);                                   /* Get new one         */
  872.                 } else {        /* Pop an environment */
  873.                         if ((int) dc.envsp <= 0) {
  874.                                 error("*** nro: cannot pop environment.\n"); break;
  875.                         }
  876.                         spval = dc.envstack[dc.envsp];  /* Current environment */
  877.                         val = dc.envstack[--dc.envsp];  /* Number of old env   */
  878.                         if (argtyp == '-') freenv(spval);/* Dump current environ*/
  879.                         else storenv(spval);                    /* ..or save it        */
  880.                         loadenv(val);                                   /* Get old one back    */
  881.                 }
  882.                 break;
  883.         case FI: /* Fill */
  884.                 dobrk();
  885.                 env.fill = YES;
  886.                 break;
  887.         case FO: /* Footer */
  888.                 gettl(infile, pg.efoot, pg.ofoot, &pg.eflim[0], &pg.oflim[0]);
  889.                 break;
  890.         case HE: /* Header */
  891.                 gettl(infile, pg.ehead, pg.ohead, &pg.ehlim[0], &pg.ohlim[0]);
  892.                 break;
  893.         case IE:
  894.         case IF:
  895.                 doieif(infile, ct & ~NOARGS);
  896.                 break;
  897.         case IN: /* Indenting */
  898.                 dobrk();
  899.                 set(&env.inval, val, argtyp, 0, 0, env.tmval-1);
  900.                 env.tival = env.inval;
  901.                 break;
  902.         case IT: /* Italic face */
  903.                 set(&env.itval, val, argtyp, 1, -1, HUGE);
  904.                 if (env.itval)  env.reqmode |= FXIT;
  905.                 else            env.reqmode &= ~FXIT;
  906.                 if (dc.bsflg != BSAMIGA)
  907.                         error("nro: italics cannot be done by overstrike :-)\n");
  908.                 break;
  909.         case JU: /* Justify */
  910.                 env.juval = YES;
  911.                 break;
  912.         case LS: /* Line spacing */
  913.                 set(&env.lsval, val, argtyp, 1, 1, HUGE);
  914.                 break;
  915.         case M1: /* Set topmost margin */
  916.                 set(&pg.m1val, val, argtyp, 2, 0, HUGE);
  917.                 break;
  918.         case M2: /* Set second top margin */
  919.                 set(&pg.m2val, val, argtyp, 2, 0, HUGE);
  920.                 break;
  921.         case M3: /* Set first bottom margin */
  922.                 set(&pg.m3val, val, argtyp, 2, 0, HUGE);
  923.                 pg.bottom = pg.plval - pg.m4val - pg.m3val;
  924.                 break;
  925.         case M4: /* Set bottom-most margin */
  926.                 set(&pg.m4val, val, argtyp, 2, 0, HUGE);
  927.                 pg.bottom = pg.plval - pg.m4val - pg.m3val;
  928.                 break;
  929.         case MACRO: /* Macro expansion */
  930.                 maceval(macexp, infile);
  931.                 break;
  932.         case NE: /* Need n lines */
  933.                 /* dobrk(); */
  934.                 if ((pg.bottom-pg.lineno+1) < (val*env.lsval)) space(pg.plval);
  935.                 break;
  936.         case NF: /* No fill */
  937.                 dobrk();
  938.                 env.fill = NO;
  939.                 break;
  940.         case NJ: /* No justify */
  941.                 env.juval = NO;
  942.                 break;
  943.         case NR: /* Set number register */
  944.                 while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  945.                 if (!isalpha(chr)) {
  946.                         error("nro: invalid or missing number register name\n");
  947.                 } else {
  948.                         ct = tolower(chr) - 'a';
  949.                         val = getval(&chr, infile);
  950.                         set(&dc.nr[ct], val, chr, 0, -HUGE, HUGE);
  951.                 }
  952.                 ct = 0;
  953.                 break;
  954.         case OF: /* Odd footer */
  955.                 gettl(infile, pg.ofoot, NULL, &pg.oflim[0], NULL);
  956.                 break;
  957.         case OH: /* Odd header */
  958.                 gettl(infile, pg.ohead, NULL, &pg.ohlim[0], NULL);
  959.                 break;
  960.         case PC: /* Page number character */
  961.                 while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  962.                 if (iseol(chr)) env.pgchr = EOS;
  963.                 else { env.pgchr = chr; ct = 0; }
  964.                 break;
  965.         case PL: /* Page length */
  966.                 set(&pg.plval, val, argtyp, PAGELEN,
  967.                         pg.m1val+pg.m2val+pg.m3val+pg.m4val+1, HUGE);
  968.                 pg.bottom = pg.plval - pg.m3val - pg.m4val;
  969.                 break;
  970.         case PN: /* Page numbering mode */
  971.                 set(&env.pnflg, val, argtyp, PNARABIC, PNARABIC, PNUROMAN);
  972.                 break;
  973.         case PO: /* Page offset */
  974.                 set(&pg.offset, val, argtyp, 0, 0, HUGE);
  975.                 break;
  976.         case RM: /* Right margin */
  977.                 set(&env.rmval, val, argtyp, PAGEWIDTH, env.tival+1, HUGE);
  978.                 env.tmval = env.rmval;
  979.                 break;
  980.         case SO: /* Source file */
  981.                 if (getcmdwrd(word, infile) == EOF) break;
  982.                 skipeol(infile);
  983.                 ct = NOARGS;
  984.                 if (dc.flevel+1 >= NFILES) {
  985.                         error("nro: .so commands nested too deeply\n");
  986.                         break;
  987.                 }
  988.                 if ((sofile[dc.flevel+1] = fopen(word, "r")) == NULL) {
  989.                         error("nro: unable to open %s\n", word);
  990.                         break;
  991.                 }
  992.                 if (verbose > 2) error("nro: processing file '%s'\n", word);
  993.                 sopbb[dc.flevel] = mac.pbb;     /* Stack push back stacks */
  994.                 mac.pbb = mac.ppb + 1;
  995.                 dc.flevel++;
  996.                 infile = sofile[dc.flevel];
  997.                 break;
  998.         case SP: /* Space */
  999.                 set(&spval, val, argtyp, 1, 0, HUGE);
  1000.                 space(spval * env.lsval);
  1001.                 break;
  1002.         case TA: /* Tab settings */
  1003.                 tabs(infile);
  1004.                 break;
  1005.         case TI: /* Temporary indent */
  1006.                 dobrk();
  1007.                 set(&env.tival, val, argtyp, 0, 0, env.tmval);
  1008.                 break;
  1009.         case TM: /* Terminal Message */
  1010.                 fflush(stdout);
  1011.                 if (env.dontbrk == FALSE || pout != stdout) {
  1012.                         while (val = ngetc(infile), isnteol(val) && val != EOF)
  1013.                                 putc(val, stderr);
  1014.                         putc('\n', stderr);
  1015.                         fflush(stderr);
  1016.                 } else ct = 0; /* Skip rest of command line */
  1017.                 break;
  1018.         case UL: /* Underline */
  1019.                 set(&env.ulval, val, argtyp, -1, 1, HUGE);
  1020.                 if (env.ulval)  env.reqmode |= FXUL;
  1021.                 else            env.reqmode &= ~FXUL;
  1022.                 if (dc.bsflg != BSAMIGA)
  1023.                         env.cuval = env.boval = 0;
  1024.                 break;
  1025.         case UN: /* Undefine macro */
  1026.                 undefmac(word, infile);
  1027.                 break;
  1028.         }
  1029.         env.dontbrk = FALSE;
  1030.  
  1031.         if (argtyp != STRINGTYP && !(ct & NOARGS))      skipeol(infile);
  1032.  
  1033.         return;
  1034. }
  1035.  
  1036.  
  1037. /*
  1038.  *      Decodes nro command and returns its associated
  1039.  *      value.
  1040.  */
  1041.  
  1042. comtyp(line, macdef)
  1043. uchar *line;
  1044. uchar **macdef;
  1045. {
  1046.         uchar c1, c2;
  1047.  
  1048.         /*
  1049.          *      First check to see if the command is a macro.
  1050.          *      If it is, return expansion in macdef.
  1051.          *      Note that upper and lower case characters are handled
  1052.          *      differently for macro names, but not for normal command names.
  1053.          */
  1054.  
  1055.         if ((*macdef = getmac(line)) != NULL) {
  1056.                 return MACRO | NOARGS;
  1057.         }
  1058.  
  1059.         c1 = tolower(*line++);
  1060.         c2 = tolower(*line  );
  1061.  
  1062.         switch (c1) {
  1063.         case '"':
  1064.         case '*':
  1065.                 return COMMENT | NOARGS;
  1066.         case 'b':
  1067.                 if              (c2 == 'o') return BO;
  1068.                 else if (c2 == 'p') return BP;
  1069.                 else if (c2 == 'r') return BR;
  1070.                 else if (c2 == 's') return BS;
  1071.                 break;
  1072.         case 'c':
  1073.                 if              (c2 == '2') return C2 | NOARGS;
  1074.                 else if (c2 == 'c') return CC | NOARGS;
  1075.                 else if (c2 == 'e') return CE;
  1076.                 else if (c2 == 'u') return CU;
  1077.                 break;
  1078.         case 'd':
  1079.                 if      (c2 == 'e') return DE | NOARGS;
  1080.                 break;
  1081.         case 'e':
  1082.                 if              (c2 == 'f') return EF | NOARGS;
  1083.                 else if (c2 == 'h') return EH | NOARGS;
  1084.                 else if (c2 == 'l') return EL | NOARGS;
  1085.                 else if (c2 == 'n') return EN;
  1086.                 else if (c2 == 'v') return EV;
  1087.                 break;
  1088.         case 'f':
  1089.                 if              (c2 == 'i') return FI;
  1090.                 else if (c2 == 'o') return FO | NOARGS;
  1091.                 break;
  1092.         case 'h':
  1093.                 if      (c2 == 'e') return HE | NOARGS;
  1094.                 break;
  1095.         case 'i':
  1096.                 if              (c2 == 'e') return IE | NOARGS;
  1097.                 else if (c2 == 'f') return IF | NOARGS;
  1098.                 else if (c2 == 'n') return IN;
  1099.                 else if (c2 == 't') return IT;
  1100.                 break;
  1101.         case 'j':
  1102.                 if      (c2 == 'u') return JU;
  1103.                 break;
  1104.         case 'l':
  1105.                 if      (c2 == 's') return LS;
  1106.                 break;
  1107.         case 'm':
  1108.                 if              (c2 == '1') return M1;
  1109.                 else if (c2 == '2') return M2;
  1110.                 else if (c2 == '3') return M3;
  1111.                 else if (c2 == '4') return M4;
  1112.                 break;
  1113.         case 'n':
  1114.                 if              (c2 == 'e') return NE;
  1115.                 else if (c2 == 'f') return NF;
  1116.                 else if (c2 == 'j') return NJ;
  1117.                 else if (c2 == 'r') return NR | NOARGS;
  1118.                 break;
  1119.         case 'o':
  1120.                 if              (c2 == 'f') return OF | NOARGS;
  1121.                 else if (c2 == 'h') return OH | NOARGS;
  1122.                 break;
  1123.         case 'p':
  1124.                 if              (c2 == 'c') return PC | NOARGS;
  1125.                 else if (c2 == 'l') return PL;
  1126.                 else if (c2 == 'n') return PN;
  1127.                 else if (c2 == 'o') return PO;
  1128.                 break;
  1129.         case 'r':
  1130.                 if      (c2 == 'm') return RM;
  1131.                 break;
  1132.         case 's':
  1133.                 if              (c2 == 'o') return SO | NOARGS;
  1134.                 else if (c2 == 'p') return SP;
  1135.                 break;
  1136.         case 't':
  1137.                 if      (c2 == 'a') return TA | NOARGS;
  1138.                 else if (c2 == 'i') return TI;
  1139.                 else if (c2 == 'm') return TM | NOARGS;
  1140.                 break;
  1141.         case 'u':
  1142.                 if              (c2 == 'l') return UL;
  1143.                 else if (c2 == 'n') return UN | NOARGS;
  1144.         }
  1145.         return UNKNOWN;
  1146. }
  1147.  
  1148.  
  1149. /*
  1150.  *      Retrieves optional argument following nro command.
  1151.  *      returns positive integer value with sign (if any)
  1152.  *      saved in character addressed by pargtyp.
  1153.  *      In case of a string, puts in a quote.
  1154.  *      It won't read the character it doesn't understand,
  1155.  *      i.e. semicolons and newlines, and garbage.
  1156.  */
  1157.  
  1158. getval(pargtyp, infile)
  1159. uchar *pargtyp;
  1160. register FILE *infile;
  1161. {
  1162.         register uchar chr, operator = '+';
  1163.         short digit;
  1164.         register int val, cumval;
  1165.  
  1166.         while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  1167.  
  1168.         *pargtyp = chr;
  1169.         if (chr == '+' || chr == '-' || chr == '/' || chr == '*')
  1170.                 chr = ngetc(infile);
  1171.         else if (chr == STRINGTYP)
  1172.                 return 1;
  1173.  
  1174.         cumval = 0;
  1175.  
  1176. again:
  1177.         val = 0;
  1178.  
  1179.         if (chr == '(') {       /* Parenthesized subexpression */
  1180.                 val = getval(&digit, infile); /* Dummy argtype pointer */
  1181.                 chr = ngetc(infile);
  1182.                 if (chr != ')')
  1183.                         error("nro: missing close parenthesis in expression\n");
  1184.                 else
  1185.                         chr = ngetc(infile); /* Get next operator or anything */
  1186.                 if (*pargtyp == '(') *pargtyp = '0';
  1187.         } else {                        /* Try to collect a number from the input */
  1188.                 while ((digit = atod(chr)) >= 0) {
  1189.                         val = 10 * val + digit;
  1190.                         chr = ngetc(infile);
  1191.                 }
  1192.         }
  1193.  
  1194.         /* Check if we need to evaluate an operator */
  1195.         if (operator) {
  1196.                 switch (operator) {
  1197.                 case '+': cumval += val; break;
  1198.                 case '-': cumval -= val; break;
  1199.                 case '*': cumval *= val; break;
  1200.                 case '/':
  1201.                         if (val != 0) cumval /= val;
  1202.                         else error("nro: division by zero\n");
  1203.                         break;
  1204.                 case '%':
  1205.                         if (val != 0) cumval %= val;
  1206.                         else error("nro: modulo by zero\n");
  1207.                         break;
  1208.                 case '<': cumval = cumval < val;  break;
  1209.                 case '=': cumval = cumval == val; break;
  1210.                 case '>': cumval = cumval > val;  break;
  1211.                 case '&': cumval = cumval & val; break;
  1212.                 case '|': cumval = cumval | val; break;
  1213.                 }
  1214.                 operator = '\0';
  1215.         }
  1216.  
  1217.         /* See if there is more to come */
  1218.         switch (chr) {
  1219.         case '+': case '-': case '*': case '/': case '%':
  1220.         case '<': case '=': case '>':
  1221.         case '&': case '|':
  1222.                 operator = chr;
  1223.                 chr = ngetc(infile);
  1224.                 goto again;
  1225.         default:
  1226.                 putbak(chr);    /* Put back what we can't interpret */
  1227.  
  1228.                 return cumval;
  1229.         }
  1230. }
  1231.  
  1232.  
  1233. /*
  1234.  *      Convert string to decimal.
  1235.  *      processes only positive values.
  1236.  */
  1237.  
  1238. ctod(p)
  1239. uchar *p;
  1240. {
  1241.         int val, d;
  1242.  
  1243.         val = 0;
  1244.         while (*p != EOS) {
  1245.                 d = atod(*p++);
  1246.                 if (d == -1) return val;
  1247.                 val = 10 * val + d;
  1248.         }
  1249.         return val;
  1250. }
  1251.  
  1252.  
  1253. /*
  1254.  *      Convert ascii character to decimal.
  1255.  */
  1256.  
  1257. atod(c)
  1258. uchar c;
  1259. {
  1260.         return ((c < '0') || (c > '9')) ? -1 : c-'0';
  1261. }
  1262.  
  1263.  
  1264. /*
  1265.  *      Get non-blank word from the input file.
  1266.  *      Returns the number of spaces skipped before the word,
  1267.  *      or EOF on end of file.
  1268.  *      It won't read past a newline or semicolon,
  1269.  *      but will skip the first space following the word.
  1270.  */
  1271.  
  1272. getcmdwrd(to, infile)
  1273. register uchar *to;
  1274. register FILE *infile;
  1275. {
  1276.         register short chr;
  1277.         int skipped = 0;
  1278.         short length = 0;
  1279.  
  1280.  
  1281.         chr = ngetc(infile);
  1282.         if (chr == EOF) {
  1283.                 *to = EOS;
  1284.                 return EOF;
  1285.         }
  1286.  
  1287.         /* Skip spaces */
  1288.  
  1289.         while (isspace(chr)) {
  1290.                 chr = ngetc(infile);
  1291.                 skipped++;
  1292.         }
  1293.  
  1294.         while (isntspace(chr) && isnteol(chr) && chr != EOF &&
  1295.                         chr != MORETXT && length < MAXWORD-3) {
  1296.                 *to++ = chr;
  1297.                 length++;
  1298.                 chr = ngetc(infile);
  1299.         }
  1300.  
  1301.         if (iseol(chr) || chr == MORETXT) putbak(chr);
  1302.  
  1303.         *to = EOS;
  1304.         if (length >= MAXWORD-3) error("nro: command word buffer overflow\n");
  1305.  
  1306.         return skipped;
  1307. }
  1308.  
  1309.  
  1310. /*
  1311.  *      Skip the rest of the current input line
  1312.  */
  1313.  
  1314. skipeol(infile)
  1315. FILE *infile;
  1316. {
  1317.         int chr;
  1318.  
  1319.         while ((chr = ngetc(infile)) != '\n' && chr != MORETXT &&
  1320.                 chr != EOF);
  1321. }
  1322.  
  1323.  
  1324. /*
  1325.  *      Process an IF or IE request
  1326.  */
  1327.  
  1328. doieif(infile, request)
  1329. FILE *infile;
  1330. int request;
  1331. {
  1332.         uchar *strp;
  1333.         short negation;
  1334.         int chr, val;
  1335.         uchar delim;
  1336.         uchar string[MAXWORD];
  1337.  
  1338.         while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  1339.         if (chr == '!') {
  1340.                 negation = TRUE;
  1341.                 chr = ngetc(infile);
  1342.         } else
  1343.                 negation = FALSE;
  1344.  
  1345.         switch (chr) {
  1346.         case 'e':       /* Even page number */
  1347.                 val = (pg.curpag % 2) == 0;
  1348.                 break;
  1349.         case 'o':       /* Odd page number */
  1350.                 val = (pg.curpag % 2) != 0;
  1351.                 break;
  1352.         case 'n':       /* Nro(ff) is formatter */
  1353.                 val = TRUE; break;
  1354.         case 't':       /* Troff certainly not */
  1355.                 val = FALSE; break;
  1356.         case '0': case '1': case '2': case '3': case '4':
  1357.         case '5': case '6': case '7': case '8': case '9':
  1358.         case '+': case '-': case '(':
  1359.                 /* Must be an expression */
  1360.                 putbak(chr);
  1361.                 chr = getval(&string[0], infile);
  1362.                 val = 0;
  1363.                 set(&val, chr, string[0], 0, -HUGE, HUGE);
  1364.                 val = val > 0;
  1365.                 break;
  1366.         default:        /* String comparison */
  1367.                 delim  = chr;
  1368.                 strp   = string;        /* Collect first string */
  1369.                 while ((chr = ngetc(infile)) != delim &&
  1370.                                 strp < string + sizeof(string) - 2)
  1371.                         *strp++ = chr;
  1372.                 *strp = EOS;
  1373.                 val = TRUE;
  1374.  
  1375.                 strp = string;  /* Compare with second string */
  1376.                 while ((chr = ngetc(infile)) != delim) {
  1377.                         if (*strp++ != chr) val = FALSE;
  1378.                 }
  1379.                 if (*strp != EOS) val = FALSE;
  1380.                 break;
  1381.         }
  1382.         if (negation) val = !val;
  1383.         if (request == IE) env.lastie = val;
  1384.  
  1385.         while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  1386.  
  1387.         if (val) {     /* Condition is true. Don't skip any text */
  1388.                 if (chr != BEGIF) putbak(chr);
  1389.         } else { /* Need to skip some text, maybe even very much */
  1390.                 if (chr == BEGIF)          /* Skip until end of line */
  1391.                         dc.iflvl = 1;
  1392.                 while (isnteol(chr)) chr = ngetc(infile);
  1393.         }
  1394. }
  1395.  
  1396.  
  1397. /*
  1398.  *      Process an EL request
  1399.  */
  1400.  
  1401. doel(infile)
  1402. FILE *infile;
  1403. {
  1404.         int chr, val;
  1405.  
  1406.         while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  1407.  
  1408.         /* Toggle last remembered condition */
  1409.         val = env.lastie = !env.lastie;
  1410.  
  1411.         if (val) {     /* Condition is true. Don't skip any text */
  1412.                 if (chr != BEGIF) putbak(chr);
  1413.         } else { /* Need to skip some text, maybe even very much */
  1414.                 if (chr == BEGIF)          /* Skip until end of line */
  1415.                         dc.iflvl = 1;
  1416.                 while (isnteol(chr)) chr = ngetc(infile);
  1417.         }
  1418. }
  1419.  
  1420.  
  1421. /*
  1422.  *      Process a TA request
  1423.  */
  1424.  
  1425. tabs(infile)
  1426. FILE *infile;
  1427. {
  1428.         int chr, val, i, j;
  1429.  
  1430.         while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  1431.  
  1432.         if (iseol(chr) || chr == EOF) { /* No arguments */
  1433.                 for (i=0; i<MAXTAB; i++)
  1434.                         env.tabstop[i] = NOTAB;
  1435.                 return;
  1436.         }
  1437.  
  1438.         i = env.inval;
  1439.  
  1440.         for (;;) {      /* Collect the values */
  1441.                 putbak(chr);
  1442.                 val = getval((uchar *)&chr, infile);
  1443.  
  1444.                 if (*(uchar *)&chr == '+') {    /* Saves a temporary uchar :-) */
  1445.                         val += i;
  1446.                 }
  1447.  
  1448.                 /* Find where to insert the tab */
  1449.                 i=0;
  1450.                 while (i<MAXTAB && env.tabstop[i] < val) i++;
  1451.  
  1452.                 if (i < MAXTAB && env.tabstop[i] != val)  {
  1453.                         /* Insert the tab */
  1454.                         for (j=MAXTAB-1; j > i; j--)
  1455.                                 env.tabstop[j] = env.tabstop[j-1];
  1456.  
  1457.                         env.tabstop[i] = val;
  1458.                 }
  1459.                 i = val;
  1460.  
  1461.                 while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  1462.                 if (iseol(chr) || chr == MORETXT || chr == EOF) break;
  1463.         }
  1464. }
  1465.  
  1466.  
  1467.  
  1468. /*
  1469.  *      Loadenv - get an environment
  1470.  */
  1471.  
  1472. loadenv(number)
  1473. int number;
  1474. {
  1475.         struct environ *envp;
  1476.         short freeit = env.dontbrk;
  1477.  
  1478.         if (number < 0 || number >= NUMENV) return ERR;
  1479.  
  1480.         envp = environ[number];
  1481.  
  1482.         if (envp) {     /* It't there. Copy it. */
  1483.                 env = *envp;
  1484.                 if (freeit) {           /* We may free the saved image of it, */
  1485.                         freenv(number); /* if we are tight on memory.         */
  1486.                 }
  1487.         } else {        /* It isn't. Just use default values. */
  1488.                 initenv();
  1489.         }
  1490.  
  1491.         return OK;
  1492. }
  1493.  
  1494. /*
  1495.  *      Storenv - save an environment
  1496.  */
  1497.  
  1498. storenv(number)
  1499. int number;
  1500. {
  1501.         struct environ *envp;
  1502.  
  1503.         if (number < 0 || number >= NUMENV) return ERR;
  1504.  
  1505.         envp = environ[number];
  1506.  
  1507.         if (envp == NULL) {     /* Never saw this guy before */
  1508.                 if ((envp = (struct environ *)malloc(sizeof(*envp))) == NULL) {
  1509.                         error("*** nro: cannot allocate environment #%d\n", number);
  1510.                         return ERR;
  1511.                 } else if (verbose > 2)
  1512.                         error("nro: allocated environment #%d\n", number);
  1513.                 environ[number] = envp;
  1514.         }
  1515.  
  1516.         *envp = env;
  1517.  
  1518.         return OK;
  1519. }
  1520.  
  1521.  
  1522. /*
  1523.  *      Freenv - throw an environment away
  1524.  */
  1525.  
  1526. freenv(number)
  1527. int number;
  1528. {
  1529.         struct environ *envp;
  1530.  
  1531.         if (number < 0 || number >= NUMENV) return ERR;
  1532.  
  1533.         envp = environ[number];
  1534.  
  1535.         if (envp) {
  1536.                 free(envp);
  1537.                 environ[number] = NULL;
  1538.                 if (verbose > 2) error("nro: freed environment #%d\n", number);
  1539.         }
  1540.  
  1541.         return OK;
  1542. }
  1543.  
  1544.  
  1545. /*
  1546.  *      End current filled line
  1547.  */
  1548.  
  1549. dobrk()
  1550. {
  1551.         if (!env.dontbrk) {             /* Breaks may be disabled by using .'xx */
  1552.                 if (env.outp > 0) {
  1553. #ifdef CPM
  1554.                         env.outbuf[env.outp++] = '\r';
  1555.                         env.outbuf[env.outp++] = '\n';
  1556.                         env.outbuf[env.outp  ] = EOS;
  1557. #else  CPM
  1558.                         env.outbuf[env.outp++] = '\n';
  1559.                         env.outbuf[env.outp  ] = EOS;
  1560. #endif CPM
  1561.                         if (env.ceval != 0)     /* Centering */
  1562.                                 center(env.outbuf);
  1563.                         put(env.outbuf);
  1564.                 }
  1565.                 env.outp = 0;
  1566.                 env.outw = 0;
  1567.                 env.outwds = 0;
  1568.         }
  1569. }
  1570.  
  1571.  
  1572.  
  1573. /*
  1574.  *      Define a macro
  1575.  */
  1576.  
  1577. defmac(word, infile)
  1578. uchar *word;
  1579. FILE *infile;
  1580. {
  1581.         uchar line[MAXLINE];
  1582.  
  1583.         getcmdwrd(word, infile);
  1584.         skipeol(infile);
  1585.  
  1586.         if (word[0] == EOS && verbose)
  1587.                 error("nro: appending to macro definition\n");
  1588.  
  1589.         while (getlin(line, infile) != EOF) {
  1590.                 if (line[0] == env.cmdchr && line[1] == 'e' && line[2] == 'n')
  1591.                         break;
  1592.                 if (putmac(word, line) == ERR) {
  1593.                         error("*** nro: macro definition table full\n");
  1594.                 }
  1595.                 word[0] = EOS;
  1596.         }
  1597. }
  1598.  
  1599.  
  1600. /*
  1601.  *      Put macro definition into table.
  1602.  *      If name == "", concatenate this definition to the previous one.
  1603.  */
  1604.  
  1605. putmac(name, def)
  1606. uchar *name;
  1607. uchar *def;
  1608. {
  1609.         int lenofname, lenofdef;
  1610.  
  1611.         lenofname = strlen(name);
  1612.         lenofdef = strlen(def);
  1613.  
  1614.         if ((lenofname && mac.lastp >= mac.mxmdef) ||
  1615.                 (mac.emb + lenofname + lenofdef + 2 > &mac.mb[mac.macbuf])) {
  1616.                 return ERR;
  1617.         }
  1618.         if (lenofname) {
  1619.                 ++mac.lastp;
  1620.                 mac.mnames[mac.lastp] = mac.emb;
  1621.                 strcpy(mac.emb, name);
  1622.                 mac.emb += lenofname + 1;
  1623.         } else
  1624.                 mac.emb--;
  1625.  
  1626.         strcpy(mac.emb, def);
  1627.         mac.emb += lenofdef + 1;
  1628.         return OK;
  1629. }
  1630.  
  1631.  
  1632.  
  1633. /*
  1634.  *      Get macro definition from table
  1635.  */
  1636.  
  1637. uchar *getmac(name)
  1638. uchar *name;
  1639. {
  1640.         register int i;
  1641.         register uchar *name1, *mname;
  1642.  
  1643.  
  1644.         for (i = mac.lastp; i > 0; --i) {       /*V1.5*/
  1645.                 name1 = name;
  1646.                 mname = mac.mnames[i];
  1647.                 while (*(name1++) == *(mname++)) {
  1648.                         if (name1[-1] == EOS)   return mname;
  1649.                 }
  1650.         }
  1651.         return NULL;
  1652. }
  1653.  
  1654.  
  1655. /*
  1656.  *      Delete macro definition from table
  1657.  */
  1658.  
  1659. undefmac(word, infile)
  1660. uchar *word;
  1661. FILE *infile;
  1662. {
  1663.         register int i;
  1664.         register uchar *name, *mname;
  1665.  
  1666.         getcmdwrd(word, infile);
  1667.         skipeol(infile);
  1668.  
  1669.         for (i = mac.lastp; i >= 0; --i) {
  1670.                 name = word;
  1671.                 mname = mac.mnames[i];
  1672.                 while (*(name++) == *(mname++)) {
  1673.                         if (name[-1] == EOS) {
  1674.                                 mac.lastp = i-1;
  1675.                                 mac.emb = mac.mnames[i];
  1676.                                 return OK;
  1677.                         }
  1678.                 }
  1679.         }
  1680.         return ERR;
  1681. }
  1682.  
  1683.  
  1684.  
  1685. /*
  1686.  *      Evaluate macro expansion for re-evaluation
  1687.  */
  1688.  
  1689. maceval(macdef, infile)
  1690. register uchar *macdef;
  1691. FILE *infile;
  1692. {
  1693.         uchar *argp[10];
  1694.         int i;
  1695.         uchar c;
  1696.         uchar line[MAXLINE];
  1697.         register uchar *linep = line;
  1698.  
  1699.         *linep++ = EOS;
  1700.         getlin(linep, infile);
  1701.         /*
  1702.          *       Initialize argp array to substitute empty string
  1703.          *       string for any undefined argument
  1704.          */
  1705.         for (i=0; i<10; ++i) argp[i] = line;
  1706.  
  1707.         for (i=0; i<10; ++i) {
  1708.                 linep = skipbl(linep);
  1709.                 if (iseol(*linep) || *linep == MORETXT || *linep == EOS) break;
  1710.                 if (*linep == '\'' || *linep == '"') {
  1711.                         c = *linep++;
  1712.                         argp[i] = linep;
  1713.                         while (*linep != c && isnteol(*linep) && *linep != EOS)
  1714.                                 linep++;
  1715.                         *linep++ = EOS;
  1716.                 } else {
  1717.                         argp[i] = linep;
  1718.                         linep = skipwd(linep);
  1719.                         *linep++ = EOS;
  1720.                 }
  1721.         }
  1722.  
  1723.         /* First push back any remaining text or commands */
  1724.  
  1725.         if (*linep == MORETXT) pbstr(linep+1);
  1726.  
  1727.         /* Now push back macro body */
  1728.  
  1729.         for (i=strlen(macdef)-1; i>=0; --i) {
  1730.                 /* Need we substitute an argument? */
  1731.                 if (i > 0 && macdef[i-1] == '$') {
  1732.                         if (!isdigit(macdef[i]) || macdef[i-2] == '$') {
  1733.                                 /* Re-evaluate escape characters from macro body */
  1734.                                 putbak(macdef[i] | NOGUARD);
  1735.                         } else {
  1736.                                 /* but not of any arguments */
  1737.                                 pbstr(argp[macdef[i]-'0']);
  1738.                                 --i;
  1739.                         }
  1740.                 } else {
  1741.                         putbak(macdef[i] | NOGUARD);
  1742.                 }
  1743.         }
  1744. }
  1745.  
  1746.  
  1747. /*
  1748.  *      Push back string into input stream for re-evaluation
  1749.  */
  1750.  
  1751. pbstr(p)
  1752. register uchar p[];
  1753. {
  1754.         register int i;
  1755.  
  1756.         for (i=strlen(p)-1; i>=0; --i) {
  1757.                 putbak(p[i]);
  1758.         }
  1759. }
  1760.  
  1761.  
  1762.  
  1763. /*
  1764.  *      Push character back into input stream
  1765.  */
  1766.  
  1767. putbak(c)
  1768. int c;
  1769. {
  1770.         if (mac.ppb < mac.pbb) {
  1771.                 mac.ppb = mac.pbb;
  1772.                 *mac.ppb = c;
  1773.         } else {
  1774.                 if (mac.ppb >= mac.pbbend-2) {
  1775.                         error("*** nro: push back buffer overflow\n");
  1776.                 }
  1777.                 *++mac.ppb = c;
  1778.         }
  1779.  
  1780.         /* Avoid evaluating escaped escape characters twice */
  1781.         if (c == ESCCHAR)
  1782.                 *++mac.ppb = ESCCHAR;
  1783. }
  1784.  
  1785.  
  1786.  
  1787. /*
  1788.  *      Get header or footer title
  1789.  */
  1790.  
  1791. gettl(infile, line1, line2, limit1, limit2)
  1792. FILE *infile;
  1793. uchar *line1, *line2;
  1794. int limit1[], limit2[];
  1795. {
  1796.         uchar c;
  1797.  
  1798.         while (c = ngetc(infile), isspace(c));
  1799.  
  1800.         line1[0] = c;
  1801.         if (isnteol(c)) getlin(line1+1, infile);
  1802.         limit1[LEFT] = env.inval;
  1803.         limit1[RIGHT] = env.rmval;
  1804.         if (line2) {
  1805.                 strcpy(line2, line1);
  1806.                 limit2[LEFT] = limit1[LEFT];
  1807.                 limit2[RIGHT] = limit1[RIGHT];
  1808.         }
  1809. }
  1810.  
  1811.  
  1812.  
  1813. /*
  1814.  *      Set parameter and check range
  1815.  */
  1816.  
  1817. set(param, val, type, defval, minval, maxval)
  1818. int *param;
  1819. int val;
  1820. uchar type;
  1821. int defval, minval, maxval;
  1822. {
  1823.         switch(type) {
  1824.         case '+':
  1825.                 *param += val; break;
  1826.         case '-':
  1827.                 *param -= val; break;
  1828.         case '*':
  1829.                 *param *= val; break;
  1830.         case '/':
  1831.                 if (val != 0) *param /= val;
  1832.                 else error ("nro: division by zero modification\n");
  1833.                 break;
  1834.         case '%':
  1835.                 if (val != 0) *param %= val;
  1836.                 else error ("nro: modulo by zero modification\n");
  1837.                 break;
  1838.         default:
  1839.                 *param = isdigit(type)? val : defval;
  1840.                 break;
  1841.         }
  1842.         if (*param < minval)
  1843.                 *param = minval;
  1844.         else if (*param > maxval)
  1845.                 *param = maxval;
  1846. }
  1847.  
  1848.  
  1849.  
  1850. /*
  1851.  *      Skip blanks and tabs in character buffer.
  1852.  *      return pointer to first non-space char.
  1853.  */
  1854.  
  1855. uchar *skipbl(p)
  1856. uchar *p;
  1857. {
  1858.         while (isspace(*p)) ++p;
  1859.         return p;
  1860. }
  1861.  
  1862.  
  1863. /*
  1864.  *      Skip over word and punctuation
  1865.  */
  1866.  
  1867. uchar *skipwd(p)
  1868. uchar *p;
  1869. {
  1870.         while (isntspace(*p) && isnteol(*p) && *p != EOS)
  1871.                 ++p;
  1872.         return p;
  1873. }
  1874.  
  1875.  
  1876.  
  1877. /*
  1878.  *      Space vertically n lines
  1879.  */
  1880.  
  1881. space(n)
  1882. int n;
  1883. {
  1884.         dobrk();
  1885.         if (pg.lineno > pg.bottom) return;
  1886.         if (pg.lineno == 0) phead();
  1887.         skip(min(n, pg.bottom+1-pg.lineno));
  1888.         pg.lineno += n;
  1889.         if (pg.lineno > pg.bottom) pfoot();
  1890. }
  1891.  
  1892. SHAR_EOF
  1893. #    End of shell archive
  1894. exit 0
  1895. -- 
  1896. Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
  1897. Have five nice days.
  1898.